﻿/*************************************************************************************
 *
 * 文 件 名： Worker
 * 描    述： 从模板新增
 * 
 * 版    本： V1.0
 * 创 建 者： YSW
 * 创建时间： 2022-08-24 8:54:29
 * ======================================
 * 历史更新记录
 * 版本：V          修改时间：         修改人：
 * 修改内容：
 * ======================================
*************************************************************************************/

using CommonModel.Model;
using CommonModel.Util;
using Dm.filter.log;
using GXRTBTC.PublicClassLibrary;
using GXRTBTC.PublicClassLibrary.Field;
using GXRTBTC.PublicClassLibrary.Model;
using GXRTBTC.PublicClassLibrary.Redis;
using GXRTBTC.PublicClassLibrary.Util;
using Microsoft.Extensions.Logging;
using NetTaste;
using NewLife;
using NewLife.Data;
using NewLife.Messaging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Npgsql;
using RedisHelp;
using StackExchange.Redis;
using System.Collections.Concurrent;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using static Dm.net.buffer.ByteArrayBuffer;
using EnumHelper = GXRTBTC.PublicClassLibrary.Util.EnumHelper;

namespace GXRTBTC.WebCommandIssueService
{
    /// <summary>
    /// 后台服务
    /// 使用后台服务，让服务已启动就对Worker进行初始化
    /// </summary>
    public class MyBackgroundService : Microsoft.Extensions.Hosting.BackgroundService
    {
        /// <summary>
        /// 实例Worker这里引用只是为了让其初始化
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="worker"></param>
        public MyBackgroundService(Worker worker) { }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.CompletedTask;
        }
    }

    /// <summary>
    /// 后台服务
    /// </summary>
    public class Worker
    {
        #region 参数
        /// <summary>
        /// 当前节点名称
        /// 组成：节点所在IP+容器ID
        /// </summary>
        //public string nodeName { get; set; } = TransForm.GetContainerAddress() + "_" + Environment.MachineName;
        public string nodeName { get; set; } = TransForm.GetHostIPOrName() + "_" + Environment.MachineName;
        /// <summary>
        /// 日志
        /// </summary>
        public readonly ILogger mLogger;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration mConfiguration;
        /// <summary>
        /// StackExchange.Redis
        /// </summary>
        private RedisHelper redis { get; set; }
        //private RedisHelper redisPower { get; set; }
        //private RedisHelper redisPowerData { get; set; }
        private RedisHelper redisTask { get; set; }
        private RedisHelper redisIssue { get; set; }

        private Object syncLock = new object();
        /// <summary>
        /// 接收包数量
        /// </summary>
        private long pkgsRcv { get; set; } = 0;
        /// <summary>
        /// 是否接收到数据
        /// </summary>
        private bool isRcvData { get; set; } = false;

        /// <summary>
        /// 接收数据总量
        /// </summary>
        private long numsRcv { get; set; } = 0;
        private long cmdSend { get; set; } = 0;
        /// <summary>
        /// 实时电源复位器的连接台站数量
        /// </summary>
        private int powerStationCount { get; set; } = 0;

        /// <summary>
        /// 接收数据缓存
        /// </summary>
        //private BlockingCollection<OperateModel> rcvCacheNormal { get; set; } = null;
        /// <summary>
        /// 加速缓存，和rcvCacheNormal区分开来，以免加速影响操作速度
        /// </summary>
        private BlockingCollection<OperateModel> rcvCacheQuick { get; set; } = null;
        //private BlockingCollection<OperateModel> rcvCacheTask { get; set; } = null;
        private long powerSendCount { get; set; } = 0;

        /// <summary>
        /// 配置信息
        /// </summary>
        private Dictionary<string, SchemaDocument> schemaDict { get; set; } = new Dictionary<string, SchemaDocument>();
        /// <summary>
        /// 设置模型
        /// </summary>
        private Dictionary<string, Dictionary<string, Dictionary<string, string>>> setitem_model { get; set; } = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();

        private string snmpGet { get; set; } = "10.71.31.220:4021";

        /// <summary>
        /// 最小线程池数量
        /// </summary>
        private int minPoolNum { get; set; } = 10;
        /// <summary>
        /// 最大线程池数量
        /// </summary>
        private int maxPoolNum { get; set; } = 100;

        private long timeOutNum { get; set; } = 0;

        /// <summary>
        /// 副本数，默认只有自身
        /// </summary>
        private int nodeNum { get; set; } = 1;
        /// <summary>
        /// 当前副本编号
        /// </summary>
        private int nodeNo { get; set; } = 0;

        /// <summary>
        /// 台站，工位号对应设备信息
        /// </summary>
        private Dictionary<string, Dictionary<string, DeviceInfo>> stationPositionDeviceInfos { get; set; } = new Dictionary<string, Dictionary<string, DeviceInfo>>();
        /// <summary>
        /// 工位号和频率对应
        /// </summary>
        public Dictionary<string, Dictionary<string, Dictionary<string, string>>> positionIdToFrqs { get; set; } = new();
        /// <summary>
        /// 模板信息
        /// </summary>
        private Dictionary<string, Dictionary<string, string>> issueTemplates { get; set; } = new Dictionary<string, Dictionary<string, string>>();
        /// <summary>
        /// 模板参数信息
        /// </summary>
        private Dictionary<string, Dictionary<string, string>> issuePara { get; set; } = new Dictionary<string, Dictionary<string, string>>();
        /// <summary>
        /// 设备下发模板信息
        /// </summary>
        private Dictionary<string, Dictionary<string, Dictionary<string, string>>> devIssueInfo { get; set; } = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
        /// <summary>
        /// 特殊处理的设备类型
        /// 目前只有0x0101的状态数据（cmd_type=1）读取时
        /// 加速间隔为300毫秒
        /// 默认是1000毫秒（1秒）
        /// </summary>
        private Dictionary<string, int> specDevice { get; set; } = new Dictionary<string, int>() { { "0x0101", 1 } };

        /// <summary>
        /// 配置集
        /// </summary>
        private Dictionary<string, string> paramInfo { get; set; } = new();

        /*******************电源复位器参数*************************/
        //private string powerServer { get; set; } = "10.170.1.61";
        //private string powerPort { get; set; } = "40002";

        //private ITcpClient powerClient { get; set; } = null;
        /*******************电源复位器参数*************************/

        /// <summary>
        /// 数据库连接配置
        /// </summary>
        private DbConnection dbConnection { get; set; } = null;
        private bool IsDbConn { get; set; } = false;

        private HttpClientUtil httpClientUtil { get; set; }
        private string dataUrl { get; set; } = "ws://111.111.8.242:8002/data/1.0/1/0101_svrrcv_1/0";
        private string webUrl { get; set; } = "ws://111.111.8.242:8001/data/1.0/1/0101_svrrcv_1/0";
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="configuration"></param>
        public Worker(ILogger<Worker> logger, IConfiguration configuration)
        {
            this.mLogger = logger;
            this.mConfiguration = configuration;

            //给通用日志赋值
            CommonUtil.mLogger = logger;

            Init();
        }

        #region 初始化
        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            //设置线程池
            System.Threading.ThreadPool.SetMinThreads(minPoolNum, minPoolNum);
            System.Threading.ThreadPool.SetMaxThreads(maxPoolNum, maxPoolNum);

            //redis初始化
            string redisConnString = "";
            try
            {
               
#if DEBUG
                redisConnString = RsaEncrypt.DecryptFree(mConfiguration["RedisConnStringDebug"]);
#else
                redisConnString = RsaEncrypt.DecryptFree(mConfiguration["RedisConnString"]);
#endif
                redis = new RedisHelper(redisConnString, mLogger);
                redisTask = new RedisHelper(redisConnString, mLogger);
                redisIssue = new RedisHelper(redisConnString, mLogger);
                //redisPower = new RedisHelper(redisConnString, mLogger);
                //redisPowerData = new RedisHelper(redisConnString, mLogger);
            }
            catch (Exception e)
            {
                mLogger.LogInformation($"redis连接【{redisConnString}】异常，" + e);
            }

            //从缓存读取设置
            paramInfo = redis.HashGetAllDict<string>("paramInfo");

            httpClientUtil = new HttpClientUtil(mLogger);
            dbConnection = InitDb();

            //rcvCacheNormal = new BlockingCollection<OperateModel>();
            rcvCacheQuick = new BlockingCollection<OperateModel>();
            //rcvCacheTask = new BlockingCollection<OperateModel>();

            //powerServer = paramInfo.GetValueOrDefault("PowerResetAddr", "10.170.1.61");
            //powerPort = paramInfo.GetValueOrDefault("PowerResetPort", "40002");

            //从redis读取缓存信息
            InitCacheInfo();

            InitThread();
        }

        /// <summary>
        /// 数据库初始化
        /// </summary>
        private DbConnection InitDb()
        {
            DbConnection? dbconn = null;
            try
            {
                //添加解密
                dbconn = new NpgsqlConnection(RsaEncrypt.DecryptFree(mConfiguration["PgSQLConnString"]));
                dbconn.Open();
                IsDbConn = true;
                mLogger.LogInformation("pgSQL数据库连接成功！");
            }
            catch (Exception e)
            {
                IsDbConn = false;
                mLogger.LogError("pgSQL数据库连接失败！" + e.Message);
            }

            return dbconn;
        }

        /// <summary>
        /// 检查数据库连接
        /// </summary>
        /// <param name="dbConnection"></param>
        private void CheckDbState(DbConnection dbConnection)
        {
            try
            {
                DbUtil.ExecReader(dbConnection, "select 1 col1", false);
                if (dbConnection.State != ConnectionState.Open)
                {
                    dbConnection.Close();
                    dbConnection.Open();
                }
                IsDbConn = true;
            }
            catch (Exception e)
            {
                IsDbConn = false;
                mLogger.LogError("数据库连接失败！" + e.Message);
            }
        }

        /// <summary>
        /// 启动线程
        /// </summary>
        private void InitThread()
        {
            mLogger.LogInformation("服务已启动");

            new Thread(TimerInit).Start();
            //new Thread(TaskCmdSend).Start();   //测试下发

            //new Thread(PowerCheckConn).Start();   //电源复位器读取、控制
            //new Thread(PowerResetRead).Start();   //电源复位器读取、控制
            //new Thread(PowerResetReStart).Start();   //电源复位器服务重启

            ////new Thread(StationSpeed).Start();   //台站加速
            new Thread((ThreadStart)(delegate { StationSpeed("1", "stationSpeed"); })).Start();    //总览状态数据加速
            new Thread((ThreadStart)(delegate { StationSpeed("1", "stationSpeed", true); })).Start();    //总览状态数据加速
            new Thread((ThreadStart)(delegate { StationSpeed("2", "stationSpeedCfg"); })).Start();    //频点等配置加速
            //new Thread(DealData).Start();   //数据处理线程
            //new Thread((ThreadStart)(delegate { DealData(rcvCacheNormal); })).Start();
            new Thread((ThreadStart)(delegate { DealData(rcvCacheQuick); })).Start();
            //new Thread((ThreadStart)(delegate { DealData(rcvCacheTask); })).Start();
            new Thread(HeartBeatTask).Start();
            new Thread(TimerTask).Start();
        }

        /// <summary>
        /// 初始化信息
        /// </summary>
        private void TimerInit()
        {
            Thread.Sleep(60 * 1000);
            while (true)
            {
                //redis缓存信息初始化
                InitCacheInfo();
                CheckDbState(dbConnection);
                Thread.Sleep(60 * 1000);
            }
        }

        /// <summary>
        /// redis缓存信息初始化
        /// </summary>
        private void InitCacheInfo()
        {
            try
            {
                paramInfo = redis.HashGetAllDict<string>("paramInfo");
                dataUrl = paramInfo.GetValueOrDefault("ReceiveDataUrl", "ws://111.111.8.242:8002/data/1.0/1/0101_svrrcv_1/0");
                webUrl = paramInfo.GetValueOrDefault("WebReceiveDataUrl", "ws://111.111.8.242:8001/data/1.0/1/0101_svrrcv_1/0");

                //台站工位号设备信息
                Dictionary<string, Dictionary<string, DeviceInfo>> tmpStationPositionDeviceInfos = redis.HashGetAllDict<Dictionary<string, DeviceInfo>>("station_position_deviceInfos");
                if (tmpStationPositionDeviceInfos != null && tmpStationPositionDeviceInfos.Count > 0)
                {
                    lock (stationPositionDeviceInfos)
                    {
                        stationPositionDeviceInfos = tmpStationPositionDeviceInfos;
                    }
                }

                //模板信息
                Dictionary<string, Dictionary<string, string>> tmpIssueTemplates = redis.HashGetAllDict<Dictionary<string, string>>("issueTemplates");
                if (tmpIssueTemplates != null && tmpIssueTemplates.Count > 0)
                {
                    lock (issueTemplates)
                    {
                        issueTemplates = tmpIssueTemplates;
                    }
                }

                //模板参数信息
                Dictionary<string, Dictionary<string, string>> tmpIssuePara = redis.HashGetAllDict<Dictionary<string, string>>("issueparas");
                if (tmpIssuePara != null && tmpIssuePara.Count > 0)
                {
                    lock (issuePara)
                    {
                        issuePara = tmpIssuePara;
                    }
                }

                if (setitem_model.Count > 0)
                {
                    foreach (string mid in setitem_model.Keys)
                    {
                        lock (setitem_model)
                        {
                            setitem_model[mid] = redis.HashGet<Dictionary<string, Dictionary<string, string>>>("setitem_model", mid);
                        }
                    }
                }

                //设备下发模板信息
                Dictionary<string, Dictionary<string, Dictionary<string, string>>> tmpDevIssueInfo = redis.HashGetAllDict<Dictionary<string, Dictionary<string, string>>>("devIssueInfo");
                if (tmpDevIssueInfo != null && tmpDevIssueInfo.Count > 0)
                {
                    lock (devIssueInfo)
                    {
                        devIssueInfo = tmpDevIssueInfo;
                    }
                }

                Dictionary<string, SchemaDocument> tmpSchemaDict = redis.HashGetAllDict<SchemaDocument>("pre_analysis_config");
                if (tmpSchemaDict != null && tmpSchemaDict.Count > 0)
                {
                    lock (schemaDict)
                    {
                        schemaDict = tmpSchemaDict.Where(d => d.Key.ToUpper().Equals("0X0708_SNMP_GETBASICMSG")).ToDictionary(d => d.Key, d => d.Value);
                    }
                }

                //工位号和频率对应
                Dictionary<string, Dictionary<string, Dictionary<string, string>>> tmpPositionIdToFrqs = redis.HashGetAllDict<Dictionary<string, Dictionary<string, string>>>("positionIdToFrq");
                if (tmpPositionIdToFrqs != null && tmpPositionIdToFrqs.Count > 0)
                {
                    lock (tmpPositionIdToFrqs)
                    {
                        positionIdToFrqs = tmpPositionIdToFrqs;
                    }
                }
            }
            catch (Exception e)
            {
                mLogger.LogInformation("更新获取台站信息缓存失败，" + e);
            }
        }
        #endregion

        #region 辅助方法
        ///// <summary>
        ///// 拼接日志插入sql
        ///// </summary>
        ///// <param name="operateModel"></param>
        ///// <returns></returns>
        //private string GetInsLogTaosSql(OperateModel operateModel)
        //{
        //    StringBuilder sql = new StringBuilder();
        //    sql.Append("insert into t_operate_log(ts, position_id, stationCode, sevicesn, device, ip_addr, op_port, log_info, command, op_code, op_time) values(")
        //        .Append("'" + string.Format("{0:yyyy-MM-dd HH:mm:ss.ffffff}", DateTime.Now) + "'")
        //        .Append(",'" + operateModel.position_id + "'")
        //        .Append(",'" + operateModel.deviceInfo.stationCode + "'")
        //        .Append(",'" + operateModel.deviceInfo.seviceSN + "'")
        //        .Append(",'" + operateModel.deviceInfo.deviceType + "'")
        //        .Append(",'" + operateModel.deviceInfo.ip_addr + "'")
        //        .Append("," + operateModel.deviceInfo.port)
        //        .Append(",'" + operateModel.logInfo + "'")
        //        .Append(",'" + operateModel.cmd + "'")
        //        .Append(",'" + operateModel.op_code + "'")
        //        .Append(",'" + string.Format("{0:yyyy-MM-dd HH:mm:ss.ffffff}", operateModel.op_time) + "'")
        //        .Append(")");
        //    return sql.ToString();
        //}

        /// <summary>
        /// 拼接日志插入sql
        /// </summary>
        /// <param name="operateModel"></param>
        /// <returns></returns>
        //private string GetInsPgsqlLogSql(OperateModel operateModel)
        //{
        //    StringBuilder sql = new StringBuilder();
        //    sql.Append("insert into operation_log(user_id, user_name, ip_addr, station_id, postion_id, operate_detail, log_type, para_data, frequency, real_name) values(")
        //        .Append("'" + operateModel.op_code + "'")
        //        .Append(",'" + operateModel.op_name + "'")
        //        .Append(",'" + operateModel.op_ipaddr + "'")
        //        .Append(",'" + operateModel.deviceInfo.stationCode + "'")
        //        .Append(",'" + operateModel.deviceInfo.position_id + "'")
        //        .Append(",'" + operateModel.logInfo + "'")
        //        .Append("," + operateModel.op_type + "")
        //        .Append(",'" + JsonConvert.SerializeObject(operateModel.parasValues) + "'")
        //        .Append(",'" + operateModel.frequency + "'")
        //        .Append(",'" + operateModel.real_name + "'")
        //        .Append(")");

        //    mLogger.LogInformation(operateModel.op_name + "(realname=" + operateModel.real_name + " userCode=" + operateModel.op_code + ")来自(ip=" + operateModel.op_ipaddr +
        //        ") 对工位号=" + operateModel.deviceInfo.position_id + ("".Equals(operateModel.frequency) ? "" : "频率=" + operateModel.frequency) + " 执行【" + operateModel.logInfo + "】");
        //    return sql.ToString();
        //}

        /// <summary>
        /// GetInsTaskLogSql
        /// </summary>
        /// <param name="operateModel"></param>
        /// <returns></returns>
        //private string GetInsTaskLogSql(OperateModel operateModel)
        //{
        //    string sql = "insert into task_detail_log(task_id,oper_key,station_id,position_id,ip_addr,dev_port,cmd_name,op_time,create_time,create_node,memo) values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}')";
        //    sql = string.Format(sql, operateModel.task_id,
        //        operateModel.oper_key,
        //        operateModel.deviceInfo.stationCode,
        //        operateModel.position_id,
        //        operateModel.deviceInfo.ip_addr,
        //        operateModel.deviceInfo.port,
        //        operateModel.cmd_name,
        //        TransForm.DateTimeFormat(operateModel.op_time),
        //        TransForm.DateTimeFormat(operateModel.create_time),
        //        operateModel.create_node,
        //        operateModel.logInfo
        //        );

        //    mLogger.LogInformation(operateModel.op_name + "(realname=" + operateModel.real_name + " userCode=" + operateModel.op_code + ")来自(ip=" + operateModel.op_ipaddr +
        //        ") 对工位号=" + operateModel.deviceInfo.position_id + ("".Equals(operateModel.frequency) ? "" : "频率=" + operateModel.frequency) + " 执行【" + operateModel.logInfo + "】");
        //    return sql.ToString();
        //}
        #endregion

        #region 电源复位器相关操作
        /// <summary>
        /// 电源复位器重启
        /// </summary>
        //        private void PowerResetReStart()
        //        {
        //            Thread.Sleep(1000);

        //            IUdpNode udpNode = null;
        //            string server = paramInfo.GetValueOrDefault("PowerResetAddr", "10.170.1.61");
        //            string port = paramInfo.GetValueOrDefault("PowerResetPortReStart", "7575");
        //            while (true)
        //            {
        //                try
        //                {
        //                    //只需要运行一次，在其中一个节点即可
        //                    if (nodeNo == 0)
        //                    {
        //                        //是否重启电源复位器服务
        //                        bool reStart = redisPowerData.KeyExists("PowerResetReStart");
        //                        if (reStart)
        //                        {
        //                            if (udpNode == null)
        //                            {
        //                                udpNode = new UdpNode();
        //                            }
        //                            if (udpNode != null && udpNode.State != ServiceState.Started)
        //                            {
        //                                udpNode.Start();
        //                            }

        //                            //重启操作有效期
        //                            DateTime dtReStart = redisPowerData.StringGet<DateTime>("PowerResetReStart");
        //                            if ((DateTime.Now - dtReStart).TotalSeconds < 10)
        //                            {
        //                                if (udpNode != null)
        //                                {
        //                                    byte[] cmd = Encoding.Default.GetBytes("ReStationDevServerApp01");
        //#if DEBUG

        //#else
        //                                    udpNode.Send(server, ushort.Parse(port), cmd, cmd.Length);
        //#endif
        //                                    mLogger.LogInformation("电源复位器重启");

        //                                }
        //                            }

        //                            redisPowerData.KeyDelete("PowerResetReStart");
        //                        }
        //                    }
        //                }
        //                catch (Exception e)
        //                {
        //                    mLogger.LogError("电源复位器重启异常，" + e);
        //                }
        //                finally
        //                {
        //                    Thread.Sleep(5);
        //                }
        //            }
        //        }

        //        private void PowerCheckConn()
        //        {
        //            redisPower.StringSetAsync("PowerResetConnect", 0);
        //            Thread.Sleep(200);
        //            while (true)
        //            {
        //                try
        //                {
        //                    //只需要运行一次，在其中一个节点即可
        //                    if (nodeNo == 0)
        //                    {
        //                        CheckConn();
        //                    }
        //                }
        //                catch (Exception e)
        //                {
        //                    mLogger.LogError("电源复位器重启异常，" + e);
        //                }
        //                finally
        //                {
        //                    Thread.Sleep(1 * 1000);
        //                }
        //            }
        //        }

        //        /// <summary>
        //        /// 电源复位器读取
        //        /// </summary>
        //        private void PowerResetRead()
        //        {
        //            Thread.Sleep(1000);
        //            string readStations = "";
        //            Dictionary<string, DateTime> sedRec = new();
        //            while (true)
        //            {
        //                try
        //                {
        //                    //测试
        //#if DEBUG
        //                    redisPower.HashSet("PowerResetRead", "0608", new Dictionary<string, object>() { { "datetime", DateTime.Now.AddSeconds(10) }, { "addrs", "150,154,151" } });
        //#endif

        //                    //只需要运行一次，在其中一个节点即可
        //                    if (nodeNo == 0)
        //                    {
        //                        //string tmpReadStations = "";
        //                        ////读取
        //                        //try
        //                        //{
        //                        //    Dictionary<string, Dictionary<string, Object>> readDict = redisPower.HashGetAllDict<Dictionary<string, Object>>("PowerResetRead");
        //                        //    if (readDict != null && readDict.Count > 0)
        //                        //    {
        //                        //        //有效期
        //                        //        readDict = readDict.Where(d => ((DateTime)d.Value["datetime"]) > DateTime.Now).ToDictionary(d => d.Key, d => d.Value);
        //                        //        if (readDict.Count > 0)
        //                        //        {
        //                        //            int powerStationCountPre = powerStationCount;
        //                        //            powerStationCount = readDict.Count;
        //                        //            if (powerStationCountPre == 0)
        //                        //            {
        //                        //                CheckConn();
        //                        //            }

        //                        //            tmpReadStations = JsonConvert.SerializeObject(readDict.Keys);
        //                        //            foreach (string stationId in readDict.Keys)
        //                        //            {
        //                        //                List<string> addrs = readDict[stationId]["addrs"].ToString().Split(',').ToList();
        //                        //                foreach (string addr in addrs)
        //                        //                {
        //                        //                    string recKey = stationId + "_" + addr;
        //                        //                    if (!sedRec.ContainsKey(recKey))
        //                        //                    {
        //                        //                        sedRec.TryAdd(recKey, DateTime.Now);
        //                        //                    }

        //                        //                    if ((DateTime.Now - sedRec[recKey]).TotalMilliseconds > 500)
        //                        //                    {
        //                        //                        bool rs = false;
        //                        //                        for (int i = 0; i < 10; i++)
        //                        //                        {
        //                        //                            if (rs)
        //                        //                            {
        //                        //                                break;
        //                        //                            }

        //                        //                            rs = PowerResetRead(stationId, int.Parse(addr));
        //                        //                        }
        //                        //                        Thread.Sleep(2);

        //                        //                        if (rs)
        //                        //                        {
        //                        //                            sedRec[recKey] = DateTime.Now;
        //                        //                        }
        //                        //                    }
        //                        //                }
        //                        //            }
        //                        //        }
        //                        //        else
        //                        //        {
        //                        //            powerStationCount = 0;
        //                        //        }
        //                        //    }
        //                        //    else
        //                        //    {
        //                        //        powerStationCount = 0;
        //                        //    }
        //                        //}
        //                        //catch (Exception ex)
        //                        //{
        //                        //    mLogger.LogError("电源复位器读取异常，" + ex);
        //                        //}
        //                        //finally
        //                        //{
        //                        //    if (!tmpReadStations.Equals(readStations))
        //                        //    {
        //                        //        readStations = tmpReadStations;
        //                        //        mLogger.LogInformation("电源复位器读取=" + readStations);
        //                        //    }
        //                        //}

        //                        //读取
        //                        string tmpReadStations = PowerResetRead(sedRec);
        //                        //记录读取台站变化
        //                        if (!tmpReadStations.Equals(readStations))
        //                        {
        //                            readStations = tmpReadStations;
        //                            mLogger.LogInformation("电源复位器读取=" + readStations);
        //                        }

        //                        //控制
        //                        PowerResetCtrl(sedRec);
        //                    }
        //                    else
        //                    {
        //                        powerStationCount = 0;
        //                    }
        //                }
        //                catch (Exception e)
        //                {
        //                    mLogger.LogError("电源复位器下发异常，" + e);
        //                }
        //                finally
        //                {
        //                    Thread.Sleep(5);
        //                }
        //            }
        //        }

        //        /// <summary>
        //        /// 电源复位器读取
        //        /// </summary>
        //        /// <param name="sedRec"></param>
        //        /// <returns></returns>
        //        private string PowerResetRead(Dictionary<string, DateTime> sedRec)
        //        {
        //            string tmpReadStations = "";
        //            try
        //            {
        //                //读取
        //                Dictionary<string, Dictionary<string, Object>> readDict = redisPower.HashGetAllDict<Dictionary<string, Object>>("PowerResetRead");
        //                if (readDict != null && readDict.Count > 0)
        //                {
        //                    //有效期
        //                    readDict = readDict.Where(d => ((DateTime)d.Value["datetime"]) > DateTime.Now).ToDictionary(d => d.Key, d => d.Value);
        //                    if (readDict.Count > 0)
        //                    {
        //                        int powerStationCountPre = powerStationCount;
        //                        powerStationCount = readDict.Count;
        //                        if (powerStationCountPre == 0)
        //                        {
        //                            mLogger.LogInformation("有台站连接，开始连接");
        //                            CheckConn();
        //                        }

        //                        tmpReadStations = JsonConvert.SerializeObject(readDict.Keys);
        //                        foreach (string stationId in readDict.Keys)
        //                        {
        //                            List<string> addrs = readDict[stationId]["addrs"].ToString().Split(',').ToList();
        //                            foreach (string addr in addrs)
        //                            {
        //                                string recKey = stationId + "_" + addr;
        //                                if (!sedRec.ContainsKey(recKey))
        //                                {
        //                                    sedRec.TryAdd(recKey, DateTime.Now);
        //                                }
        //                                if (!sedRec.ContainsKey(stationId))
        //                                {
        //                                    sedRec.TryAdd(stationId, DateTime.Now);
        //                                }

        //                                if ((DateTime.Now - sedRec[recKey]).TotalMilliseconds > 500 && (DateTime.Now - sedRec[stationId]).TotalMilliseconds > 50)
        //                                {
        //                                    Thread.Sleep(2);
        //                                    bool rs = false;
        //                                    for (int i = 0; i < 10; i++)
        //                                    {
        //                                        if (rs)
        //                                        {
        //                                            break;
        //                                        }

        //                                        rs = PowerResetRead(stationId, int.Parse(addr));
        //                                    }

        //                                    if (rs)
        //                                    {
        //                                        lock (syncLock)
        //                                        {
        //                                            powerSendCount += 1;
        //                                        }
        //                                        sedRec[recKey] = DateTime.Now;
        //                                        sedRec[stationId] = DateTime.Now;
        //                                    }
        //                                }
        //                            }
        //                        }
        //                    }
        //                    else
        //                    {
        //                        powerStationCount = 0;
        //                    }
        //                }
        //                else
        //                {
        //                    powerStationCount = 0;
        //                }
        //            }
        //            catch (Exception ex)
        //            {
        //                mLogger.LogError("电源复位器读取异常，" + ex);
        //            }

        //            return tmpReadStations;
        //        }

        //        /// <summary>
        //        /// 电源复位器控制
        //        /// </summary>
        //        private void PowerResetCtrl(Dictionary<string, DateTime> sedRec)
        //        {
        //            DateTime dtNow = DateTime.Now;
        //            //控制
        //            try
        //            {
        //                Dictionary<string, DateTime> ctrlDict = redisPower.HashGetAllDict<DateTime>("PowerResetCtrl");
        //                if (ctrlDict != null && ctrlDict.Count > 0)
        //                {
        //                    //直接筛选
        //                    ctrlDict = ctrlDict.Where(d => d.Value > dtNow).ToDictionary(d => d.Key, d => d.Value);
        //                    foreach (string stationId in ctrlDict.Keys)
        //                    {
        //                        //有效期
        //                        if (ctrlDict[stationId] > dtNow)
        //                        {
        //                            Dictionary<string, Dictionary<string, string>> ctrlInfos = redisPower.HashGetAllDict<Dictionary<string, string>>("PowerResetCtrl_" + stationId);
        //                            if (ctrlInfos != null && ctrlInfos.Count > 0)
        //                            {
        //                                if (!sedRec.ContainsKey(stationId))
        //                                {
        //                                    sedRec.TryAdd(stationId, DateTime.Now);
        //                                }

        //                                foreach (string key in ctrlInfos.Keys)
        //                                {
        //                                    Dictionary<string, string> ctrlInfo = ctrlInfos[key];
        //                                    string addr = ctrlInfo["addr"];
        //                                    string param = ctrlInfo["param"];
        //                                    if (ctrlInfo.ContainsKey("sendTime"))
        //                                    {
        //                                        continue;
        //                                    }

        //                                    if ((DateTime.Now - sedRec[stationId]).TotalMilliseconds < 50)
        //                                    {
        //                                        continue;
        //                                    }

        //                                    //经过测试，前后一定要有一定间隔才能确保基本下发成功
        //                                    //具体原因猜测可能是服务那边处理速度的问题
        //                                    //发送
        //                                    Thread.Sleep(2);
        //                                    bool rs = false;
        //                                    for (int i = 0; i < 10; i++)
        //                                    {
        //                                        if (rs)
        //                                        {
        //                                            break;
        //                                        }

        //                                        rs = PowerResetCtrl(stationId, int.Parse(addr), param);
        //                                    }

        //                                    //控制只下发一次，下发后马上删除
        //                                    if (rs)
        //                                    {
        //                                        lock (syncLock)
        //                                        {
        //                                            powerSendCount += 1;
        //                                        }

        //                                        //redisPower.HashDeleteAsync("PowerResetCtrl_" + stationId, key);

        //                                        //不删除，新增发送时间
        //                                        ctrlInfo.TryAdd("sendTime", TransForm.GetFormatDatetime());
        //                                        redisPower.HashSetAsync("PowerResetCtrl_" + stationId, key, ctrlInfo);
        //                                    }
        //                                }
        //                            }
        //                        }
        //                    }
        //                }
        //            }
        //            catch (Exception ex)
        //            {
        //                mLogger.LogError("电源复位器控制异常，" + ex);
        //            }
        //        }

        //        /// <summary>
        //        /// 判断打开连接
        //        /// </summary>
        //        /// <param name="client"></param>
        //        /// <param name="server"></param>
        //        /// <param name="port"></param>
        //        /// <param name="tag"></param>
        //        private void CheckConn()
        //        {
        //            if (powerStationCount > 0)
        //            {
        //                try
        //                {
        //                    string conn = redisPower.StringGet("PowerResetConnect") ?? "0";
        //                    if (powerClient == null)
        //                    {
        //                        powerClient = CreateTcpClient(powerServer, int.Parse(powerPort));
        //                    }
        //                    else if (powerClient != null && ((powerClient.State != ServiceState.Started && powerClient.State != ServiceState.Starting) || "0".Equals(conn)))
        //                    {
        //                        mLogger.LogInformation("powerClient状态：" + powerClient.State + "，重新连接 ...");
        //                        powerClient.Connect();
        //                    }

        //                    if (powerClient.State != ServiceState.Started && powerClient.State != ServiceState.Starting)
        //                    {
        //                        mLogger.LogError("powerClient连接异常，状态：" + powerClient.State);
        //                        redisPower.StringSetAsync("PowerResetConnect", 0);
        //                    }
        //                }
        //                catch (Exception e)
        //                {
        //                    mLogger.LogError("powerClient连接异常，" + e.Message);
        //                    redisPower.StringSetAsync("PowerResetConnect", 0);
        //                }
        //            }
        //            else
        //            {
        //                try
        //                {
        //                    if (powerClient != null)
        //                    {
        //                        mLogger.LogInformation("无台站连接，主动断开连接");
        //                        powerClient.Dispose();
        //                        powerClient = null;
        //                    }
        //                }
        //                catch (Exception e)
        //                {
        //                    mLogger.LogError("powerClient断开异常，" + e.Message);
        //                }
        //                finally
        //                {
        //                    powerClient = null;
        //                    redisPower.StringSetAsync("PowerResetConnect", 0);
        //                }
        //            }
        //        }

        //        /// <summary>
        //        /// 读取
        //        /// </summary>
        //        /// <param name="stationId"></param>
        //        /// <param name="addr"></param>
        //        /// <returns></returns>
        //        public bool PowerResetRead(string stationId, int addr)
        //        {
        //            bool rs = false;
        //            try
        //            {
        //                HjGetDate hjGetDate = new HjGetDate();

        //                /****************获取参数****************************************/
        //                //电源复位器获取参数结构体
        //                hjGetDate.StationCode = stationId;
        //                hjGetDate.SystemType = "Restorer";
        //                hjGetDate.DevAddr = (byte)addr;
        //                hjGetDate.CmdType = "GetRestorerData";
        //                hjGetDate.ParamByteCount = 0;

        //                int len = Marshal.SizeOf(hjGetDate);
        //                byte[] cmd = TransForm.StructToBytes(hjGetDate);
        //                //校验时要减去最后两位
        //                short s = (short)Array.ConvertAll(cmd.Skip(0).Take(len - 2).ToArray(), new Converter<byte, int>(((byte b) => (int)b))).Sum();
        //                hjGetDate.CheckSum = s;

        //                cmd = TransForm.StructToBytes(hjGetDate);

        //                //发送读取命令
        //                //redisPowerData.StringSet("PowerResetReadTime", DateTime.Now);
        //                redisPowerData.StringSetAsync("PowerResetReadTime", DateTime.Now);

        //                if (powerClient != null && powerClient.State == ServiceState.Started)
        //                {
        //                    lock (powerClient)
        //                    {
        //                        rs = powerClient.Send(cmd, cmd.Length);
        //                    }
        //                }
        //                else
        //                {
        //                    mLogger.LogError("电源复位器读取命令下发异常！");
        //                }
        //                //mLogger.LogInformation("电源复位器控制读取=" + JsonConvert.SerializeObject(hjGetDate));
        //                //mLogger.LogInformation("发送 " + TransForm.GetFormatDatetime("HH:mm:ss") + " " + hjGetDate.StationCode + "_" + hjGetDate.DevAddr);
        //                /****************获取参数****************************************/
        //            }
        //            catch (Exception e)
        //            {
        //                mLogger.LogError("电源复位器读取命令下发异常，" + e);
        //            }

        //            return rs;
        //        }

        //        /// <summary>
        //        /// 控制
        //        /// </summary>
        //        /// <param name="stationId"></param>
        //        /// <param name="addr"></param>
        //        /// <param name="param"></param>
        //        /// <returns></returns>
        //        public bool PowerResetCtrl(string stationId, int addr, string param)
        //        {
        //            bool rs = false;
        //            try
        //            {
        //                HjYjCmd hjYjCmd = new HjYjCmd();

        //                /****************控制命令****************************************/
        //                hjYjCmd.StationCode = stationId;
        //                hjYjCmd.SystemType = "Restorer";
        //                hjYjCmd.DevAddr = (byte)addr;
        //                hjYjCmd.CmdType = "ControlCmd";
        //                //hjYjCmd.Params = "PowerOn_1"; //PowerOn、PowerOff、Reset
        //                hjYjCmd.Params = param;
        //                hjYjCmd.ParamByteCount = 20;

        //                int len = Marshal.SizeOf(hjYjCmd);
        //                byte[] cmd = TransForm.StructToBytes(hjYjCmd);
        //                //校验时要减去最后两位
        //                short s = (short)Array.ConvertAll(cmd.Skip(0).Take(len - 2).ToArray(), new Converter<byte, int>(((byte b) => (int)b))).Sum();
        //                hjYjCmd.CheckSum = s;

        //                cmd = TransForm.StructToBytes(hjYjCmd);

        //                //控制命令
        //                if (powerClient != null && powerClient.State == ServiceState.Started)
        //                {
        //                    lock (powerClient)
        //                    {
        //                        rs = powerClient.Send(cmd, cmd.Length);
        //                    }

        //                    if (rs)
        //                    {
        //                        mLogger.LogInformation("电源复位器控制命令=" + JsonConvert.SerializeObject(hjYjCmd));
        //                    }
        //                }
        //                else
        //                {
        //                    mLogger.LogError("电源复位器控制命令下发异常！");
        //                }
        //                /****************控制命令****************************************/
        //            }
        //            catch (Exception e)
        //            {
        //                mLogger.LogError("电源复位器控制命令下发异常，" + e);
        //            }

        //            return rs;
        //        }

        //        /// <summary>
        //        /// 创建TCP侦听 -示例
        //        /// </summary>
        //        /// <param name="localAddr">地址</param>
        //        /// <param name="port">端口</param>
        //        /// <returns></returns>
        //        public ITcpClient CreateTcpClient(string addr, int port)
        //        {
        //            ITcpClient client = null;
        //            try
        //            {
        //                //TCP客户端
        //                client = new TcpClient();

        //                // <2>配置TCPServer的参数
        //                client.Address = addr;      // IP
        //                client.Port = (ushort)port;      // 端口
        //                client.SocketBufferSize = 4096;  // 缓存4K

        //                // <3> 绑定事件
        //                client.OnPrepareConnect += OnPrepareConnect;  // 准备连接了事件
        //                client.OnConnect += OnConnect;                // 连接事件
        //                client.OnHandShake += OnHandShake;            // TCP握手成功事件
        //                client.OnSend += OnSend;                      // 数据包发送事件
        //                client.OnReceive += OnReceive;                // 数据包到达事件
        //                client.OnClose += OnClose;                    // TCP连接关闭事件

        //                client.Connect();  // 连接

        //                mLogger.LogInformation("TcpClient初始化连接：" + addr + ":" + port);
        //            }
        //            catch (Exception e)
        //            {
        //                client = null;
        //                mLogger.LogError("TcpClient初始化异常，" + e);
        //            }
        //            return client;
        //        }

        //        #region 事件
        //        /// <summary>
        //        /// 准备连接了事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <param name="socket">客户端Id</param>
        //        /// <returns></returns>
        //        private HandleResult OnPrepareConnect(HPSocket.IClient sender, IntPtr socket)
        //        {
        //            sender.ExtraData = string.Empty;  // 设置附加数据（用来做粘包处理）

        //            return HandleResult.Ok;
        //        }

        //        /// <summary>
        //        /// 连接事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <returns></returns>
        //        private HandleResult OnConnect(HPSocket.IClient sender)
        //        {
        //            try
        //            {
        //                mLogger.LogInformation(string.Format("TCP客户端([{0}]{1}:{2})接入TCP服务器{3}:{4}", sender.ConnectionId, sender.BindAddress, sender.BindPort, sender.Address, sender.Port));
        //                redisPower.StringSetAsync("PowerResetConnect", 1);
        //            }
        //            catch { }
        //            return HandleResult.Ok;
        //        }

        //        /// <summary>
        //        /// TCP握手成功事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <returns></returns>
        //        private HandleResult OnHandShake(HPSocket.IClient sender)
        //        {
        //            // 一般用不到
        //            return HandleResult.Ok;
        //        }

        //        /// <summary>
        //        /// 数据包发送事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <param name="data">数据</param>
        //        /// <returns></returns>
        //        private HandleResult OnSend(HPSocket.IClient sender, byte[] data)
        //        {
        //            //Console.WriteLine(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]：{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
        //            //mLogger.LogInformation(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]：{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
        //            return HandleResult.Ok;
        //        }

        //        /// <summary>
        //        /// 数据包到达事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <param name="data">数据</param>
        //        /// <returns></returns>
        //        private HandleResult OnReceive(HPSocket.IClient sender, byte[] data)
        //        {
        //            // <1> 获取附加数据对象
        //            if (!(sender.ExtraData is string extraDataStr))
        //            {
        //                return HandleResult.Error;
        //            }

        //            //校验
        //            int len = data.Length;
        //            if (len >= 138)
        //            {
        //                //校验时要减去最后两位
        //                short sum = (short)Array.ConvertAll(data.Skip(0).Take(len - 2).ToArray(), new Converter<byte, int>(((byte b) => (int)b))).Sum();
        //                short jy = TransForm.ByteToShort(data.Skip(len - 2).Take(2).ToArray());
        //                if (jy == sum)
        //                {
        //                    ChannelState channelState = (ChannelState)TransForm.BytesToStuct(data, typeof(ChannelState));


        //                    //收到后马上写入缓存
        //                    redisPowerData.HashSet("PowerResetData_" + channelState.StationCode, channelState.DevAddr.ToString(), channelState);
        //                    redisPowerData.StringSet("PowerResetRcvTime", DateTime.Now);
        //                    redisPowerData.HashSetAsync("PowerResetRcvTime_station", channelState.StationCode + "_" + channelState.DevAddr.ToString(), TransForm.GetFormatDatetime());

        //                    //mLogger.LogInformation("收到数据=" + JsonConvert.SerializeObject(channelState));
        //                    //mLogger.LogInformation("收到数据！");
        //                    //mLogger.LogInformation("收到 " + TransForm.GetFormatDatetime("HH:mm:ss") + " " + channelState.StationCode + "_" + channelState.DevAddr);
        //                }

        //                redisPowerData.StringSetAsync("PowerResetConnect", 1);
        //            }

        //            return HandleResult.Ok;
        //        }

        //        /// <summary>
        //        /// TCP连接关闭事件
        //        /// </summary>
        //        /// <param name="sender">客户端</param>
        //        /// <param name="socketOperation">关闭类型</param>
        //        /// <param name="errorCode">错误代码</param>
        //        /// <returns></returns>
        //        private HandleResult OnClose(HPSocket.IClient sender, SocketOperation socketOperation, int errorCode)
        //        {
        //            try
        //            {
        //                sender.ExtraData = null;  // 删除附加数据
        //                //Console.WriteLine(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
        //                mLogger.LogError(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
        //                redisPower.StringSetAsync("PowerResetConnect", 0);
        //            }
        //            catch { }
        //            return HandleResult.Ok;
        //        }
        //#endregion 事件
        #endregion

        #region ping方法
        /// <summary>
        /// 用于检查IP地址或域名是否可以使用TCP/IP协议访问(使用Ping命令),true表示Ping成功,false表示Ping失败
        /// </summary>
        /// <param name="strIpOrDName">输入参数,表示IP地址或域名</param>
        /// <returns></returns>
        public bool PingIpOrDomainName(string strIpOrDName)
        {
            bool rs;
            try
            {
                Ping ping = new Ping();
                PingOptions objPinOptions = new PingOptions();
                objPinOptions.DontFragment = true;
                int intTimeout = 120;
                //intTimeout = 1000;
                //intTimeout = 500;
                PingReply pingReply = ping.Send(strIpOrDName, intTimeout, new byte[0], objPinOptions);
                rs = pingReply.Status == IPStatus.Success;
                pingReply.TryDispose();
            }
            catch (Exception)
            {
                rs = false;
            }

            return rs;
        }
        #endregion

        #region 台站总览加速
        /// <summary>
        /// 台站总览、频点配置加速
        /// </summary>
        /// <param name="type"></param>
        /// <param name="speedKey"></param>
        /// <param name="devType">需要特殊加速处理的设备类型 特殊处理0x0101信号源状态加速，需要300毫秒超快，这里特殊处理</param>
        private void StationSpeed(string type, string speedKey, bool specFlag = false)
        {
            Thread.Sleep(10 * 1000);
            Stopwatch sw;
            Dictionary<string, DateTime> stationSpeed;
            while (true)
            {
                sw = Stopwatch.StartNew();
                try
                {
                    //获取需要加速的台站
                    //stationSpeed = redis.HashGetAllDict<DateTime>("stationSpeed");
                    stationSpeed = redis.HashGetAllDict<DateTime>(speedKey);
                    if (stationSpeed != null && stationSpeed.Count > 0)
                    {
                        int orderAllNo = 0;
                        //循环需要加速的台站
                        foreach (string stationId in stationSpeed.Keys)
                        {
                            //根据副本号分配任务，多个副本各负责一部分
                            if (orderAllNo % nodeNum == nodeNo)
                            {
                                //判断时间是否还在有效期
                                if (stationSpeed[stationId] > DateTime.Now)
                                {
                                    //在线的台站才进行加速
                                    bool stationOffLine = GetStationOffLine(stationId);
#if DEBUG
                                    //测试时默认在线，方便测试
                                    stationOffLine = false;
#endif
                                    //台站有设备数据
                                    if (!stationOffLine && stationPositionDeviceInfos.ContainsKey(stationId) && stationPositionDeviceInfos[stationId].Count > 0)
                                    {
                                        //每个工位号（设备）
                                        foreach (string positionId in stationPositionDeviceInfos[stationId].Keys)
                                        {
                                            bool positionOffLine = GetPositionOffLine(positionId);
#if DEBUG
                                            //测试时默认在线，方便测试
                                            positionOffLine = false;
#endif
                                            //设备在线才加速
                                            if (!positionOffLine && stationPositionDeviceInfos[stationId].ContainsKey(positionId))
                                            {
                                                //设备类型，根据设备类型，判断该设备是否配置了主动获取状态数据命令
                                                DeviceInfo deviceInfo = stationPositionDeviceInfos[stationId][positionId];

                                                //cmd_type 命令类型，1:状态参数 2:配置参数
                                                //工位号是否配置了主动读取命令
                                                if (devIssueInfo.ContainsKey(deviceInfo.deviceType) && devIssueInfo[deviceInfo.deviceType].ContainsKey(type))
                                                {
#if DEBUG
                                                    //方便测试
#endif

                                                    //特殊处理0x0101信号源状态加速，需要300毫秒超快，这里特殊处理
                                                    //特殊处理时，不包含在specDevice里面的，不执行
                                                    //正常处理时，包含在specDevice里面且type=1的，不执行
                                                    if ((specFlag && !specDevice.ContainsKey(deviceInfo.deviceType)) || (!specFlag && "1".Equals(type) && specDevice.ContainsKey(deviceInfo.deviceType)))
                                                    {
                                                        break;
                                                    }

#if DEBUG
                                                    //方便测试
                                                    //if ("0x0101".Equals(deviceInfo.deviceType))
                                                    //{ 

                                                    //}
#endif

                                                    //状态或者配置配置id串
                                                    string[] templateIds = devIssueInfo[deviceInfo.deviceType][type]["template_ids"].Split(',');
                                                    foreach (string templateId in templateIds)
                                                    {
                                                        //模板ID是否有效
                                                        if (issueTemplates.ContainsKey(templateId))
                                                        {
                                                            //参数明细
                                                            Dictionary<string, Dictionary<string, object>> paraDetails = new();
                                                            //参数值
                                                            List<Dictionary<string, string>> dictParasValues = new();
                                                            string dftParas = issueTemplates[templateId]["dft_paras"];  //该模板只应该有默认参数
                                                            string cmd_text = issueTemplates[templateId]["cmd_text"];
                                                            string template_memo = issueTemplates[templateId].ContainsKey("memo") ? issueTemplates[templateId]["memo"] : "";
                                                            string[] dftParaArr = issueTemplates[templateId]["dft_paras"].Split(',');
                                                            foreach (string para in dftParaArr)
                                                            {
                                                                if (issuePara.ContainsKey(para))
                                                                {
                                                                    paraDetails.TryAdd(para, issuePara[para].ToDictionary(d => d.Key, d => (object)d.Value));
                                                                }
                                                            }

                                                            //判断参数是否有需要动态填充的
                                                            foreach (string para in paraDetails.Keys)
                                                            {
                                                                Dictionary<string, string> paraDetail = paraDetails[para].ToDictionary(d => d.Key, d => d.Value.ToString());
                                                                //是否必须前台输入的参数
                                                                if (paraDetail.ContainsKey("is_need") && bool.Parse(paraDetail["is_need"]))
                                                                {
                                                                    //这里暂时只能处理频率字段
                                                                    if (paraDetail.ContainsKey("para_code") && "FREQUENCY".Equals(paraDetail["para_code"].ToUpper()))
                                                                    {
                                                                        //从redis缓存尝试获取频率信息
                                                                        if (positionIdToFrqs.ContainsKey(deviceInfo.stationCode) && positionIdToFrqs[deviceInfo.stationCode].ContainsKey(deviceInfo.position_id))
                                                                        {
                                                                            foreach (string frq in positionIdToFrqs[deviceInfo.stationCode][deviceInfo.position_id].Keys)
                                                                            {
                                                                                dictParasValues.Add(new() { { para, frq } });
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }

                                                            //多个频率时循环每一个频率依次下发
                                                            if (dictParasValues.Count > 0)
                                                            {
                                                                foreach (var parasValues in dictParasValues)
                                                                {
                                                                    //生成操作模板
                                                                    OperateModel operateModel = new OperateModel()
                                                                    {
                                                                        deviceInfo = deviceInfo,
                                                                        create_node = nodeName + "_" + (nodeNo + 1).ToString(),
                                                                        //paras = null,
                                                                        parasValues = parasValues,
                                                                        paraDetails = paraDetails,
                                                                        cmd_temp = cmd_text,
                                                                        cmd_paras = dftParas,
                                                                        position_id = positionId,
                                                                        template_id = templateId,
                                                                        template_memo = template_memo,
                                                                        logInfo = "QUICK"
                                                                    };

                                                                    //主动加速的包，直接写入缓存
                                                                    lock (rcvCacheQuick)
                                                                    {
                                                                        rcvCacheQuick.Add(operateModel);
                                                                    }
                                                                }
                                                            }
                                                            else
                                                            {
                                                                //生成操作模板
                                                                OperateModel operateModel = new OperateModel()
                                                                {
                                                                    deviceInfo = deviceInfo,
                                                                    create_node = nodeName + "_" + (nodeNo + 1).ToString(),
                                                                    //paras = null,
                                                                    parasValues = new(),
                                                                    paraDetails = paraDetails,
                                                                    cmd_temp = cmd_text,
                                                                    cmd_paras = dftParas,
                                                                    position_id = positionId,
                                                                    template_id = templateId,
                                                                    template_memo = template_memo,
                                                                    logInfo = "QUICK"
                                                                };

                                                                //主动加速的包，直接写入缓存
                                                                lock (rcvCacheQuick)
                                                                {
                                                                    rcvCacheQuick.Add(operateModel);
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //不在有效期的，移除
                                    //redis.HashDelete("stationSpeed", stationId);
                                    redis.HashDelete(speedKey, stationId);
                                }
                            }
                            orderAllNo++;
                        }
                    }
                }
                catch (Exception e)
                {
                    mLogger.LogError("台站加速异常，" + e);
                }
                finally
                {
                    int gap = (int)sw.ElapsedMilliseconds;

                    //减少间隔，提速
                    int interval = specFlag ? 300 : 1000;
                    Thread.Sleep(gap >= interval ? 1 : (interval - gap));
                }
            }
        }

        /// <summary>
        /// 判断台站是否离线
        /// </summary>
        /// <param name="checkKey"></param>
        /// <returns></returns>
        private bool GetStationOffLine(string stationId)
        {
            //是否离线
            //string val = mRedisCache.GetString("online_" + stationId);
            string val = redis.StringGet("online_" + stationId);
            if (val != null && "0".Equals(val))
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 获取工位号在线状态
        /// </summary>
        /// <param name="positionId"></param>
        /// <returns></returns>
        private bool GetPositionOffLine(string positionId)
        {
            //是否离线
            //string val = mRedisCache.GetString("online_" + positionId);
            string val = redis.StringGet("online_" + positionId);
            //获取不到值也判断为离线
            if (val != null && "0".Equals(val))
            {
                return false;
            }
            return true;
        }
        #endregion

        #region 命令生成
        /// <summary>
        /// 数据处理 生成下发命令
        /// </summary>
        private async void DealData(BlockingCollection<OperateModel> rcvCache)
        {
            Thread.Sleep(1000);
            OperateModel operateModel = null;
            while (true)
            {
                try
                {
                    while (rcvCache.Count > 0)
                    {
                        Thread.Sleep(1);
                        lock (rcvCache)
                        {
                            operateModel = rcvCache.Take();
                        }

                        //升级后的命令生成方式
                        //GetCommand(operateModel);
                        operateModel = CommandUtil.GetCommand(operateModel, mLogger);

                        if (operateModel.cmd != null && !"".Equals(operateModel.cmd))
                        {
                            //发送命令到采集
                            httpClientUtil.SendTo(JsonConvert.SerializeObject(operateModel), "command_issue", dataUrl);
                        }
                    }
                }
                catch (Exception e)
                {
                    mLogger.LogError("命令生成或写数据库日志异常，" + e);
                    mLogger.LogError("异常指令，" + (operateModel == null ? "" : JsonConvert.SerializeObject(operateModel)));
                }
                finally
                {
                    Thread.Sleep(10);
                }
            }
        }
        #endregion

        #region 打印日志
        /// <summary>
        /// 定时监视
        /// </summary>
        private void TimerTask()
        {
            long numsRcvPre = 0;    //上次数量
            long cmdSendPre = 0;    //上次数量
            int times = 0;
            while (true)
            {
                if ((numsRcv - numsRcvPre) > 0 || (cmdSend - cmdSendPre) > 0 || timeOutNum > 0 || powerSendCount > 0)
                {
                    mLogger.LogInformation(nodeName +
                        " 线程数=" + ThreadPool.ThreadCount +
                        " WS连接=" + WebsocketClientManager.websocketClient.GetCount() +
                        " 接收命令=" + (numsRcv - numsRcvPre) + " 下发命令=" + (cmdSend - cmdSendPre) + " 提交超时=" + timeOutNum +
                        " 电源复位器发送命令=" + powerSendCount +
                        " 节点数=" + nodeNum + " 当前节点号=" + (nodeNo + 1));
                }
                else
                {
                    mLogger.LogInformation(nodeName + " 节点数=" + nodeNum + " 当前节点号=" + (nodeNo + 1));
                }
                numsRcvPre = numsRcv;
                cmdSendPre = cmdSend;

                lock (syncLock)
                {
                    timeOutNum = 0;
                    powerSendCount = 0;
                }

                if (times >= 60)
                {
                    times = 0;
                }

                times++;
                Thread.Sleep(10 * 1000);
            }
        }
        #endregion

        #region 操作
        /// <summary>
        /// 获取操作所需参数明细
        /// </summary>
        /// <param name="positionId"></param>
        /// <param name="name"></param>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public string GetIssuePara(string positionId, string name, HttpContext httpContext)
        {
            string paraStr = "{}";
            try
            {
                //鉴权
                //VerifInfo verifInfo = GetVerifInfo(httpContext);
                //有控制权限、有超级权限或者不验证
                //if (verifInfo.viewer)
                if (true)
                {
                    //确保参数正常
                    if (positionId != null && !"".Equals(positionId)
                        && name != null && !"".Equals(name))
                    {
                        //新模型
                        List<Dictionary<string, string>> operateInfos = redisIssue.HashGet<List<Dictionary<string, string>>>("operateParaModelNew", positionId);
                        if (operateInfos != null && operateInfos.Count > 0)
                        {
                            Dictionary<string, string> operateDetail = null;
                            foreach (Dictionary<string, string> operateInfo in operateInfos)
                            {
                                //先从操作找
                                string operate_model_id = operateInfo["operate_model_id"];
                                //string setitem_model_id = operateInfo["setitem_model_id"];
                                Dictionary<string, Dictionary<string, string>> operateDetails = redisIssue.HashGet<Dictionary<string, Dictionary<string, string>>>("operations_model", operate_model_id);
                                if (operateDetails != null && operateDetails.ContainsKey(name.ToUpper()))
                                {
                                    operateDetail = operateDetails[name.ToUpper()];
                                }
                                if (operateDetail != null)
                                {
                                    Dictionary<string, Dictionary<string, string>> paraDetails = new Dictionary<string, Dictionary<string, string>>();
                                    //string cmdText = operateDetail["cmd_text"];
                                    string cmd_paras = operateDetail.ContainsKey("template_paras") ? operateDetail["template_paras"] : "";
                                    string op_source_code = operateDetail.ContainsKey("source_code") ? operateDetail["source_code"] ?? "" : "";
                                    if ("".Equals(cmd_paras))
                                    {
                                        cmd_paras = operateDetail["dft_paras"];
                                    }
                                    List<string> paras = cmd_paras.Split(',').ToList();
                                    if (paras.Count > 0)
                                    {
                                        Dictionary<string, Dictionary<string, string>> issuePara = new Dictionary<string, Dictionary<string, string>>();
                                        foreach (string para in paras)
                                        {
                                            if (!para.Contains('"') && TransForm.IsInt(para))
                                            {
                                                //val = mRedisCache.GetString("issuepara_" + para);
                                                Dictionary<string, string> paraDetail = redisIssue.HashGet<Dictionary<string, string>>("issueparas", para);
                                                //if (val != null && !"".Equals(val))
                                                if (paraDetail != null)
                                                {
                                                    //Dictionary<string, string> paraDetail = JsonConvert.DeserializeObject<Dictionary<string, string>>(val);
                                                    if (paraDetail.ContainsKey("is_need") && bool.Parse(paraDetail["is_need"])
                                                        && paraDetail.ContainsKey("from_svr") && !bool.Parse(paraDetail["from_svr"]))
                                                    {
                                                        //如果包含source_code，则要关联到设置表获取
                                                        string calcType = paraDetail.ContainsKey("calc_type") ? paraDetail["calc_type"] ?? "" : "";
                                                        string source_code = "";// paraDetail.ContainsKey("source_code") ? paraDetail["source_code"] ?? "" : "";
                                                                                //当前5、6、7为固定的，三种下发拼接方式
                                                        if ("5".Equals(calcType) || "6".Equals(calcType) || "7".Equals(calcType))
                                                        {
                                                            source_code = op_source_code;
                                                        }
                                                        if ("".Equals(source_code))
                                                        {
                                                            issuePara.TryAdd(para, paraDetail);
                                                        }
                                                        else
                                                        {
                                                            //从设置表获取
                                                            Dictionary<string, Dictionary<string, string>> modelParaInfos = GetModelParaInfo(setitem_model, "setitem_model", source_code);
                                                            if (modelParaInfos != null)
                                                            {
                                                                foreach (string para_name in modelParaInfos.Keys)
                                                                {
                                                                    Dictionary<string, string> modelParaInfo = modelParaInfos[para_name];
                                                                    //refer_flag为0时是参考参数用，无需返回
                                                                    if (modelParaInfo.ContainsKey("refer_flag") && "-1".Equals(modelParaInfo["refer_flag"]))
                                                                    {
                                                                        issuePara.TryAdd(para_name, modelParaInfos[para_name]);
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        paraStr = JsonConvert.SerializeObject(issuePara);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                mLogger.LogError("获取参数错误，" + e.Message);
            }
            finally
            {
                mLogger.LogError("获取" + positionId + "参数" + name);
            }

            return paraStr;
        }


        /// <summary>
        /// 操作接口
        /// </summary>
        /// <param name="positionId"></param>
        /// <param name="name"></param>
        /// <param name="paraInfo"></param>
        /// <param name="frequency"></param>
        /// <param name="clientIp"></param>
        /// <param name="userCode"></param>
        /// <param name="userName"></param>
        /// <param name="realname"></param>
        /// <param name="agentType">
        /// 0-是测试的Apifox或者Postman
        /// 1-是cs客户端（即User-Agent为空串）
        /// 2-是bs客户端
        /// </param>
        /// <param name="httpContext"></param>
        /// <param name="oper_type"></param>
        /// <returns></returns>
        public async Task<List<string>> IssueOperate(string positionId, string name, Object paraInfo, Object paraInfonew, string frequency, string clientIp, string userCode, string userName, string realname, string agentType, HttpContext httpContext, int oper_type = 0)
        {
            string rs = "";
            string logInfo = "";    //操作具体描述信息
            string oper_key = "";

            //20250526 base64解码
            string paraStr = paraInfo is string ? TransForm.FromBase64(paraInfo.ToString()) : paraInfo.ToString();
            string paraStrnew = paraInfonew is string ? TransForm.FromBase64(paraInfonew.ToString()) : paraInfonew.ToString();
            try
            {
                //鉴权
                //VerifInfo verifInfo = GetVerifInfo(httpContext);
                //有控制权限、有超级权限或者不验证
                //if (verifInfo.controler)
                if (true)
                {
                    //确保参数正常
                    if (positionId != null && !"".Equals(positionId)
                        && name != null && !"".Equals(name))
                    {
                        Dictionary<string, string> parasValues = paraStr.IsNullOrEmpty() ? new Dictionary<string, string>() : JsonConvert.DeserializeObject<Dictionary<string, string>>(paraStr).ToDictionary(d => d.Key.ToUpper(), d => d.Value);
                        Dictionary<string, string> parasValuesnew = paraStrnew.IsNullOrEmpty() ? new Dictionary<string, string>() : JsonConvert.DeserializeObject<Dictionary<string, string>>(paraStrnew).ToDictionary(d => d.Key.ToUpper(), d => d.Value);

                        //新模型
                        List<Dictionary<string, string>> operateInfos = redisIssue.HashGet<List<Dictionary<string, string>>>("operateParaModelNew", positionId);
                        if (operateInfos != null && operateInfos.Count > 0)
                        {
                            Dictionary<string, string> operateDetail = null;
                            foreach (Dictionary<string, string> operateInfo in operateInfos)
                            {
                                //先从操作找
                                string operate_model_id = operateInfo["operate_model_id"];
                                Dictionary<string, Dictionary<string, string>> operateDetails = redisIssue.HashGet<Dictionary<string, Dictionary<string, string>>>("operations_model", operate_model_id);
                                if (operateDetails != null && operateDetails.ContainsKey(name.ToUpper()))
                                {
                                    operateDetail = operateDetails[name.ToUpper()];
                                }
                                if (operateDetail != null)
                                {
                                    string rPositionId = operateInfo.ContainsKey("relation_position_id") ? operateInfo["relation_position_id"] : "";
                                    //获取设备信息，判断是否存在
                                    //有关联时，取关联的设备信息
                                    DeviceInfo deviceInfo = redisIssue.HashGet<DeviceInfo>("station_deviceInfos", "".Equals(rPositionId) ? positionId : rPositionId);
                                    if (deviceInfo != null)
                                    {
                                        if (GetPositionFromUdp(positionId))
                                        //if (deviceInfo.data_type == 0)
                                        {
                                            Dictionary<string, Dictionary<string, object>> paraDetails = new();
                                            Dictionary<string, Dictionary<string, string>> paraDetailCheck = new();
                                            string cmdText = operateDetail["cmd_text"];
                                            string cmd_paras = operateDetail.ContainsKey("template_paras") ? operateDetail["template_paras"] : "";
                                            string op_source_code = operateDetail.ContainsKey("source_code") ? operateDetail["source_code"] ?? "" : "";
                                            string template_id = operateDetail.ContainsKey("template_id") ? operateDetail["template_id"] ?? "" : "";
                                            string template_memo = "";
                                            string template_key = "";
                                            if (issueTemplates.ContainsKey(template_id))
                                            {
                                                template_memo = issueTemplates[template_id]["memo"];
                                                template_key = issueTemplates[template_id]["template_key"];
                                            }
                                            if ("".Equals(cmd_paras))
                                            {
                                                cmd_paras = operateDetail["dft_paras"];
                                            }
                                            logInfo = operateDetail["para_describe"];

                                            //保存日志时需要的参数
                                            //1-控制操作 2-配置下发
                                            int op_type = 1;
                                            //频率
                                            string freq = "";

                                            if (!"".Equals(cmd_paras))
                                            {
                                                List<string> paras = cmd_paras.Split(',').ToList();
                                                if (paras.Count > 0)
                                                {
                                                    rs = "";
                                                    //校验参数个数是否匹配
                                                    if (cmdText.Contains("{" + (paras.Count - 1) + "}"))
                                                    {
                                                        //循环获取所有应该有的参数
                                                        foreach (string para in paras)
                                                        {
                                                            if (!para.Contains('"') && TransForm.IsInt(para))
                                                            {
                                                                Dictionary<string, string> paraDetail = redisIssue.HashGet<Dictionary<string, string>>("issueparas", para);
                                                                if (paraDetail != null)
                                                                {
                                                                    //如果包含source_code，则要关联到设置表获取
                                                                    string calcType = paraDetail.ContainsKey("calc_type") ? paraDetail["calc_type"] ?? "" : "";
                                                                    // paraDetail.ContainsKey("source_code") ? paraDetail["source_code"] ?? "" : "";
                                                                    string source_code = "";
                                                                    //当前5、6、7为固定的，三种下发拼接方式
                                                                    if ("5".Equals(calcType) || "6".Equals(calcType) || "7".Equals(calcType))
                                                                    {
                                                                        source_code = op_source_code;
                                                                        //动态赋予source_code到paraDetail
                                                                        if (paraDetail.ContainsKey("source_code"))
                                                                        {
                                                                            paraDetail["source_code"] = source_code;
                                                                        }
                                                                        else
                                                                        {
                                                                            paraDetail.TryAdd("source_code", source_code);
                                                                        }
                                                                        if ("".Equals(source_code))
                                                                        {
                                                                            rs = "calcType=" + calcType + "的下发配置参数source_code缺失！";
                                                                        }
                                                                    }

                                                                    if ("".Equals(source_code))
                                                                    {
                                                                        paraDetails.TryAdd(para, paraDetail.ToDictionary(d => d.Key, d => (object)d.Value));
                                                                        paraDetailCheck.TryAdd(para, paraDetail);
                                                                    }
                                                                    else
                                                                    {
                                                                        //从设置表获取
                                                                        Dictionary<string, Dictionary<string, string>> modelParaInfos = GetModelParaInfo(setitem_model, "setitem_model", source_code);
                                                                        paraDetails.TryAdd(para, paraDetail.ToDictionary(d => d.Key, d => (object)d.Value));

                                                                        int calc_type = int.Parse(paraDetail["calc_type"]);
                                                                        int realLen = 0;
                                                                        if (modelParaInfos != null)
                                                                        {
                                                                            string rKey = para + "-" + source_code;
                                                                            //paraDetails.TryAdd(rKey, modelParaInfos.Where(d => d.Value.ContainsKey("refer_flag") && d.Value["refer_flag"].Equals("-1")).ToDictionary(d => d.Key, d => (object)d.Value));
                                                                            paraDetails.TryAdd(rKey, new Dictionary<string, object>());
                                                                            //paraDetailCheck = paraDetailCheck.Concat(modelParaInfos.Where(d => d.Value.ContainsKey("refer_flag") && d.Value["refer_flag"].Equals("-1")).ToDictionary(d => d.Key, d => d.Value)).ToDictionary(d => d.Key, d => d.Value);
                                                                            //foreach (Dictionary<string, string> modelParaInfo in paraDetails[rKey].Values)
                                                                            foreach (string para_name_source in modelParaInfos.Keys)
                                                                            {
                                                                                Dictionary<string, string> modelParaInfo = modelParaInfos[para_name_source];

                                                                                //refer_flag=-1才下发
                                                                                bool issueFlag = modelParaInfo.ContainsKey("refer_flag") && modelParaInfo["refer_flag"].Equals("-1");
                                                                                if (!issueFlag)
                                                                                {
                                                                                    continue;
                                                                                }

                                                                                //循环操作
                                                                                string loop_para_name = modelParaInfo.ContainsKey("loop_para_name") ? modelParaInfo["loop_para_name"].ToUpper() : "";
                                                                                //parno循环，初始化只有一个parnoSource
                                                                                Dictionary<string, string> relationParaInfos = new() { { para_name_source, "" } };

                                                                                //1~3级台站环境下发，0x0706数字电视下发，等设置表下发有参数循环的会用上
                                                                                /************循环关联同一级参数********************/
                                                                                Dictionary<int, int> loopVals = null;
                                                                                if (!"".Equals(loop_para_name))
                                                                                {
                                                                                    loopVals = new();
                                                                                    string[] loopArr = loop_para_name.Split(',');
                                                                                    for (int i = 0; i < loopArr.Length; i++)
                                                                                    {
                                                                                        if (parasValues.ContainsKey(loopArr[i]) && !"".Equals(parasValues[loopArr[i]].ToString())
                                                                                            && TransForm.IsInt(parasValues[loopArr[i]].ToString()) && int.Parse(parasValues[loopArr[i]].ToString()) > 0)
                                                                                        {
                                                                                            loopVals.TryAdd(i, int.Parse(parasValues[loopArr[i]].ToString()));
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                            loopVals.TryAdd(i, 1);
                                                                                        }
                                                                                    }
                                                                                }
                                                                                //替换parno中的动态参数
                                                                                if (loopVals != null && loopVals.Count > 0)
                                                                                {
                                                                                    Dictionary<int, Dictionary<string, string>> tmpRelationParaInfos = new() { { 0, relationParaInfos } };
                                                                                    for (int i = 0; i < loopVals.Count; i++)
                                                                                    {
                                                                                        tmpRelationParaInfos.TryAdd(i + 1, new());
                                                                                        for (int j = 0; j < loopVals[i]; j++)
                                                                                        {
                                                                                            foreach (string paraNameIn in tmpRelationParaInfos[i].Keys)
                                                                                            {
                                                                                                tmpRelationParaInfos[i + 1].TryAdd(paraNameIn + "_" + j, tmpRelationParaInfos[i][paraNameIn].Replace("{" + i + "}", j.ToString()));
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                    relationParaInfos = tmpRelationParaInfos[loopVals.Count];
                                                                                }
                                                                                /************循环关联同一级参数********************/

                                                                                foreach (string para_name in relationParaInfos.Keys)
                                                                                {
                                                                                    paraDetails[rKey].TryAdd(para_name, modelParaInfo);
                                                                                    paraDetailCheck.TryAdd(para_name, modelParaInfo);

                                                                                    int paraLength = TransForm.IsInt(modelParaInfo["para_length"]) ? int.Parse(modelParaInfo["para_length"]) : 1;
                                                                                    if (calc_type == 5)
                                                                                    {
                                                                                        realLen += 2 + 1 + paraLength;
                                                                                    }
                                                                                    else if (calc_type == 6)
                                                                                    {
                                                                                        realLen += paraLength;
                                                                                    }
                                                                                    else if (calc_type == 7)
                                                                                    {
                                                                                        realLen += 2 + 2 + paraLength;
                                                                                    }
                                                                                }

                                                                                //int paraLength = TransForm.IsInt(modelParaInfo["para_length"]) ? int.Parse(modelParaInfo["para_length"]) : 1;
                                                                                //if (calc_type == 5)
                                                                                //{
                                                                                //    realLen += 2 + 1 + paraLength;
                                                                                //}
                                                                                //else if (calc_type == 6)
                                                                                //{
                                                                                //    realLen += paraLength;
                                                                                //}
                                                                                //else if (calc_type == 7)
                                                                                //{
                                                                                //    realLen += 2 + 2 + paraLength;
                                                                                //}
                                                                            }
                                                                        }
                                                                        //赋值实际长度
                                                                        ((Dictionary<string, object>)paraDetails[para])["para_length"] = realLen.ToString();

                                                                        //有从设置表读取，说明是配置下发
                                                                        op_type = 2;
                                                                    }
                                                                }
                                                            }
                                                        }

                                                        foreach (string para in paraDetailCheck.Keys)
                                                        {
                                                            Dictionary<string, string> paraDetail = paraDetailCheck[para];
                                                            //para_code和para_name用于匹配找到频率字段
                                                            string para_code = paraDetail.ContainsKey("para_code") ? paraDetail["para_code"].ToUpper() : "";
                                                            string para_name = paraDetail.ContainsKey("para_name") ? paraDetail["para_name"].ToUpper() : "";
                                                            string para_describe = paraDetail.ContainsKey("para_describe") ? paraDetail["para_describe"].ToUpper() : "";

                                                            //如果包含source_code，则要关联到设置表获取
                                                            bool isRelate = !paraDetail.ContainsKey("is_need");
                                                            if (isRelate ||
                                                                (paraDetail.ContainsKey("is_need") && bool.Parse(paraDetail["is_need"])
                                                                && paraDetail.ContainsKey("from_svr") && !bool.Parse(paraDetail["from_svr"])))
                                                            {
                                                                //判断是否带参数
                                                                if (parasValues.ContainsKey(para))
                                                                {
                                                                    string para_type = paraDetail["para_type"].ToUpper();
                                                                    int para_length = TransForm.IsInt(paraDetail["para_length"]) ? int.Parse(paraDetail["para_length"]) : 1;
                                                                    string para_value = parasValues[para];
                                                                    if ("FREQUENCY".Equals(para_code.ToUpper()) || "FREQUENCY".Equals(para_name.ToUpper()))
                                                                    {
                                                                        freq = para_value;
                                                                    }

                                                                    if (para_value == null || "".Equals(para_value))
                                                                    {
                                                                        rs = "参数[para]中[" + para + "]的值不能为空类型";
                                                                        break;
                                                                    }
                                                                    else if ("UCHAR".Equals(para_type) || "INT".Equals(para_type))
                                                                    {
                                                                        if (!TransForm.IsInt(para_value))
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]不是正确的[" + para_type + "]类型";
                                                                            break;
                                                                        }
                                                                    }
                                                                    else if ("ENUM".Equals(para_type))
                                                                    {
                                                                        if (!TransForm.IsInt(para_value.Replace("0X", "").Replace("0x", "")))
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]不是正确的[" + para_type + "]类型";
                                                                            break;
                                                                        }
                                                                    }
                                                                    else if ("IP".Equals(para_type))
                                                                    {
                                                                        if (!TransForm.IsIP(para_value))
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]不是正确的[" + para_type + "]类型";
                                                                            break;
                                                                        }
                                                                    }
                                                                    else if ("FLOAT".Equals(para_type))
                                                                    {
                                                                        if (!TransForm.IsDecimalSign(para_value))
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]不是正确的[" + para_type + "]类型";
                                                                            break;
                                                                        }
                                                                    }
                                                                    else if ("TIMEARRAY".Equals(para_type))
                                                                    {
                                                                        //格式 一共48字节，分8段，每段6字节 如第二段为060000235959 即开始时间06:00:00 结束时间23:59:59
                                                                        //000000010000060000235959000000000000000000000000000000000000000000000000000000000000000000000000
                                                                        if (para_value.Length % (6 * 2) != 0)
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]字符串长度不等于[" + 6 * 2 + "]的倍数";
                                                                            break;
                                                                        }
                                                                        else if (TransForm.IsAllNum(para_value))
                                                                        {
                                                                            rs = "参数[para]中[" + para + "]的值[" + para_value + "]不是正确的[" + para_type + "]类型";
                                                                            break;
                                                                        }
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    rs = "参数[" + para + "]-[" + ("".Equals(para_describe) ? para_name : para_describe) + "]缺失";
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                    else
                                                    {
                                                        rs = "参数（" + cmd_paras + "）个数与命令模板（" + cmdText + "）不匹配！";
                                                    }
                                                }
                                            }

                                            if ("".Equals(rs))
                                            {
                                                //string ip = GetClientIp(httpContext);

                                                OperateModel operateModel = new OperateModel();
                                                operateModel.deviceInfo = deviceInfo;
                                                operateModel.create_node = nodeName;
                                                //operateModel.op_code = verifInfo.userCode;
                                                //operateModel.op_name = verifInfo.userName;
                                                //operateModel.real_name = verifInfo.realname;
                                                operateModel.op_code = userCode;
                                                operateModel.op_name = userName;
                                                operateModel.real_name = realname;
                                                operateModel.op_type = op_type;
                                                operateModel.frequency = (frequency != null && !"".Equals(frequency)) ? frequency : freq;
                                                operateModel.op_ipaddr = clientIp;
                                                //operateModel.paras = null;
                                                operateModel.parasValues = parasValues;
                                                operateModel.paraDetails = paraDetails;
                                                operateModel.operate_model_id = operate_model_id;
                                                operateModel.cmd_name = name;
                                                operateModel.cmd_temp = cmdText;
                                                operateModel.cmd_paras = cmd_paras;
                                                operateModel.position_id = positionId;
                                                operateModel.template_id = template_id;
                                                operateModel.template_key = template_key;
                                                operateModel.template_memo = template_memo;
                                                operateModel.logInfo = logInfo;

                                                operateModel.oper_type = oper_type;
                                                oper_key = operateModel.oper_key;


                                                //改为直接调用生成，不经过下发服务，下发服务改为只做加速
                                                operateModel = CommandUtil.GetCommand(operateModel, mLogger);

                                                //日志单独写入达梦
                                                string logSql = GetInsPgsqlLogSql(operateModel, out object[] args);
                                                DbUtil.ExecSql(dbConnection, logSql, args);

                                                mLogger.LogInformation("dataUrl=" + dataUrl);
                                                mLogger.LogInformation(JsonConvert.SerializeObject(operateModel));
                                                //往UDP数据采集服务发送请求
                                                //发送命令到采集
                                                WebApiDataRes webApiData = (WebApiDataRes)httpClientUtil.SendTo(operateModel, "command_issue", dataUrl).Result;
                                                if (!webApiData.Code.Equals(WebApiStatusCode.code_200))
                                                {
                                                    rs = webApiData.Message;
                                                }

                                                //测试数据用
                                                if (operateModel.oper_type != 0)
                                                {
                                                    mLogger.LogInformation("往" + operateModel.position_id + "发送一条测试命令，oper_key=" + operateModel.oper_key);
                                                }
                                            }
                                        }
                                        //else if (deviceInfo.data_type == 1)
                                        else
                                        {
                                            logInfo = operateDetail["para_describe"];

                                            //强制必须携带工位号
                                            if (parasValuesnew.ContainsKey("position_id"))
                                            {
                                                parasValuesnew["position_id"] = "".Equals(rPositionId) ? positionId : rPositionId;
                                            }
                                            else
                                            {
                                                parasValuesnew.TryAdd("position_id", "".Equals(rPositionId) ? positionId : rPositionId);
                                            }
                                            parasValuesnew.TryAdd("operate_type", name);

                                            //webUrl = "ws://10.70.1.190:30802/data/1.0/1/0000_svrwebrcv_1/0";
                                            mLogger.LogInformation("webUrl=" + webUrl);
                                            mLogger.LogInformation(JsonConvert.SerializeObject(parasValuesnew));

                                            WebApiDataRes webApiData = (WebApiDataRes)httpClientUtil.SendTo(parasValuesnew, "operate", webUrl).Result;
                                            if (webApiData != null && webApiData.Code.Equals(WebApiStatusCode.code_200))
                                            {
                                                rs = "";

                                                //日志单独写入达梦
                                                string logSql = GetInsPgsqlLogSqlNew(userCode, userName, clientIp, deviceInfo.stationCode, positionId, logInfo, oper_type, paraStrnew, frequency, realname, "成功", out object[] args);
                                                DbUtil.ExecSql(dbConnection, logSql, args);
                                            }
                                            else
                                            {
                                                if (webApiData != null)
                                                {
                                                    rs = webApiData.Message;
                                                }
                                                else
                                                { 
                                                    rs = "下发异常";
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        rs = "未从redis匹配到工位号[" + positionId + "]" + ("".Equals(rPositionId) ? "" : "关联工位号[" + rPositionId + "]") + "的设备信息";
                                    }

                                    break;
                                }
                            }

                            if (operateDetail == null)
                            {
                                rs = "未从redis匹配到工位号[" + positionId + "]的模型[" + name + "]明细信息";
                            }
                        }
                        else
                        {
                            rs = "未从redis匹配到工位号[" + positionId + "]的模型信息";
                        }
                    }
                    else
                    {
                        rs = "参数不正确";
                    }
                }
                else
                {
                    rs = "您没有权限";
                }
            }
            catch (Exception e)
            {
                rs = e.Message;
                mLogger.LogError("命令下发，解析错误，" + e);
            }
            finally
            {
                if ("".Equals(rs))
                {
                    mLogger.LogInformation("工位号：" + positionId + " 启动下发" + ("".Equals(rs) ? "成功" : "失败") + " 错误信息：" + rs + " 操作信息：" + logInfo);
                }
                else
                {
                    mLogger.LogError("工位号：" + positionId + " 启动下发" + ("".Equals(rs) ? "成功" : "失败") + " 错误信息：" + rs + " 操作信息：" + logInfo);
                    //错误时，把所有参数打印出来，方便后续定位错误
                    mLogger.LogInformation("name=" + name + " frequeycy=" + frequency + " paraStr=" + paraStr + " paraStrNew=" + paraStrnew);
                }
            }
            return new List<string>() { ("".Equals(rs)).ToString(), logInfo, rs, oper_key };
        }

        /// <summary>
        /// 获取工位号是否来自udp，即旧主控的UDP
        /// </summary>
        /// <param name="positionId"></param>
        /// <returns></returns>
        private bool GetPositionFromUdp(string positionId)
        {
            string jsonFlag = redisIssue.HashGet("DATA_" + positionId, "json_flag");
            if (string.IsNullOrEmpty(jsonFlag) || "0".Equals(jsonFlag))
            {
                return true;
            }

            return false;
        }

        private Dictionary<string, Dictionary<string, string>> GetModelParaInfo(Dictionary<string, Dictionary<string, Dictionary<string, string>>> model, string modelKey, string mid)
        {
            if (!model.ContainsKey(mid))
            {
                model.TryAdd(mid, redisIssue.HashGet<Dictionary<string, Dictionary<string, string>>>(modelKey, mid));
            }
            return model[mid];
        }

        /// <summary>
        /// 拼接日志插入sql
        /// </summary>
        /// <param name="operateModel"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private string GetInsPgsqlLogSql(OperateModel operateModel, out Object[] args)
        {
            string sql = "insert into operation_log(user_id,user_name,ip_addr,station_id,postion_id,operate_detail,log_type,para_data,frequency,real_name,oplog_key)values('{0}','{1}','{2}','{3}','{4}','{5}',{6},'{7}','{8}','{9}','{10}')";
            List<Object> tmpArgs = new()
            {
                operateModel.op_code,
                operateModel.op_name,
                operateModel.op_ipaddr,
                operateModel.deviceInfo.stationCode,
                operateModel.deviceInfo.position_id,
                operateModel.logInfo,
                operateModel.op_type,
                JsonConvert.SerializeObject(operateModel.parasValues),
                operateModel.frequency,
                operateModel.real_name,
                operateModel.template_key_val
            };
            args = tmpArgs.ToArray();

            return sql;
        }

        /// <summary>
        /// 拼接日志插入sql
        /// </summary>
        /// <param name="opCode"></param>
        /// <param name="opName"></param>
        /// <param name="opIpaddr"></param>
        /// <param name="stationCode"></param>
        /// <param name="positionId"></param>
        /// <param name="logInfo"></param>
        /// <param name="opType"></param>
        /// <param name="parasValues"></param>
        /// <param name="frequency"></param>
        /// <param name="realName"></param>
        /// <param name="result"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private string GetInsPgsqlLogSqlNew(
            string opCode,
                string opName,
                string opIpaddr,
                string stationCode,
                string positionId,
                string logInfo,
                int opType,
                string parasValues,
                string frequency,
                string realName,
                string result, 
                out Object[] args)
        {
            string sql = "insert into operation_log(user_id,user_name,ip_addr,station_id,postion_id,operate_detail,log_type,para_data,frequency,real_name,remarks)values('{0}','{1}','{2}','{3}','{4}','{5}',{6},'{7}','{8}','{9}','{10}')";
            List<Object> tmpArgs = new()
            {
                opCode,
                opName,
                opIpaddr,
                stationCode,
                positionId,
                logInfo,
                opType,
                parasValues,
                frequency,
                realName,
                result
            };
            args = tmpArgs.ToArray();

            return sql;
        }
        #endregion

        #region 监控系统WEB API接口
        /// <summary>
        /// 针对websocket的token验证
        /// 预留，暂无用
        /// </summary>
        /// <param name="webApiData"></param>
        /// <returns></returns>
        public bool GetValidToken(IWebApiData webApiData)
        {
            bool rs = true;
            try
            {
                //var authHeader = httpContext.Request.Headers["Authorization"];
                //var token = authHeader.FirstOrDefault()?.Split(" ").Last(); // 假设使用Bearer token
                //rs = !string.IsNullOrEmpty(token);

                //获取token，进行验证
                //string token = webApiData.Data.ToString();
            }
            catch
            {
                rs = false;
            }

            return rs;
        }

        /// <summary>
        /// 接收数据处理
        /// 这里主要是接收下行控制命令，包括自动读取设置和客户端下发的命令
        /// </summary>
        /// <param name="webApiDataFull"></param>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public Object GetDataJson(WebApiDataFull webApiDataFull, HttpContext httpContext)
        {
            var sw = Stopwatch.StartNew();
            bool rs = false;
            try
            {
                string ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("::FFFF:", "");
                ushort port = (ushort)httpContext.Connection.RemotePort;

                //固定只处理某些命令
                if (webApiDataFull.ContentReq.Command.ToUpper().Equals("some_command"))
                {

                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_404;
                    ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + "，无法响应命令【" + webApiDataFull.ContentReq.Command + "】";
                }
            }
            catch (Exception e)
            {
                ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + "," + e.Message;
            }

            return rs;
        }

        /// <summary>
        /// 参数检查
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public WebParamData CheckParam(HttpContext httpContext)
        {
            WebParamData webParamData = new WebParamData();
            string msg = "";

            try
            {
                //路径参数
                string apiver = (httpContext.Request.RouteValues["apiver"] ?? httpContext.Request.Query["apiver"]).ToString();
                string level = (httpContext.Request.RouteValues["level"] ?? httpContext.Request.Query["level"]).ToString();
                string positionid = (httpContext.Request.RouteValues["positionid"] ?? httpContext.Request.Query["positionid"]).ToString();
                string sn = (httpContext.Request.RouteValues["sn"] ?? httpContext.Request.Query["sn"]).ToString();

                if (apiver == null || "".Equals(apiver))
                {
                    msg += "，API协议版本apiver不能为空";
                }

                if (level == null || "".Equals(level))
                {
                    msg += "，级别level（SYSTEM(系统级) DEVICE(设备级)）不能为空";
                }

                if (positionid == null || "".Equals(positionid))
                {
                    msg += "，位置编号（或工位号）positionid不能为空";
                }

                else if (positionid.Split("_").Length < 3)
                {
                    msg += "，位置编号（或工位号）positionid异常，参考格式xx_xxx_xxx";
                }

                if (sn == null || "".Equals(sn))
                {
                    msg += "，系统/设备序列号sn不能为空";
                }

                webParamData.apiver = apiver;
                webParamData.level = level;
                webParamData.positionid = positionid;
                webParamData.sn = sn;
            }
            catch (Exception e)
            {
                msg += e.Message;
            }
            finally
            {
                webParamData.nowmal = string.IsNullOrEmpty(msg);
                webParamData.msg = webParamData.nowmal ? msg : msg.Substring(1);
            }

            return webParamData;
        }

        /// <summary>
        /// 检查请求参数
        /// </summary>
        /// <param name="data"></param>
        /// <param name="webApiDataRes"></param>
        /// <returns></returns>
        public bool CheckData(string data, WebApiDataFull webApiDataFull)
        {
            bool rs = false;
            string msg = "";
            try
            {
                IWebApiData webApiData = null;
                Dictionary<string, Object> dictData = null;
                if (data != null && !"".Equals(data.ToString()) && ValidJson.IsJson(data.ToString()))
                {
                    dictData = JsonConvert.DeserializeObject<Dictionary<string, Object>>(data.ToString());
                    if (webApiDataFull.IsWebSocket)
                    {
                        webApiData = JsonConvert.DeserializeObject<WebApiDataReqWS>(data.ToString());
                    }
                    else
                    {
                        webApiData = JsonConvert.DeserializeObject<WebApiDataReq>(data.ToString());
                    }

                    if (webApiData == null)
                    {
                        msg += "，数据异常";
                    }
                }
                else
                {
                    msg += "，数据异常";
                }

                //数据必须参数检查
                if (dictData != null && dictData.Count > 0)
                {
                    Dictionary<string, string> tmpDict = dictData.ToDictionary(d => d.Key.ToUpper(), d => "");
                    if (!tmpDict.ContainsKey("REQUESTID"))
                    {
                        msg += "，数据不完整，缺少RequestId（请求编号）参数";
                    }
                    if (!tmpDict.ContainsKey("COMMAND"))
                    {
                        msg += "，数据不完整，缺少Command（命令）参数";
                    }
                    if (!tmpDict.ContainsKey("VERSION"))
                    {
                        msg += "，数据不完整，缺少Version（设备或系统通信协议版本）参数";
                    }
                    if (!tmpDict.ContainsKey("DATA"))
                    {
                        msg += "，数据不完整，缺少Data数据";
                    }
                    if (!tmpDict.ContainsKey("TIMESTAMP"))
                    {
                        msg += "，数据不完整，缺少Timestamp（时间戳）参数";
                    }
                    if (webApiDataFull.IsWebSocket && !tmpDict.ContainsKey("TYPE"))
                    {
                        msg += "，数据不完整，缺少Type（消息类型）参数";
                    }
                }

                webApiDataFull.dictData = dictData;
                webApiDataFull.ContentReq = webApiData;

                //消息类型判断
                if (webApiDataFull.IsWebSocket && webApiData != null && !EnumHelper.HasValue<WebApiTypeCodeWS>(((WebApiDataReqWS)webApiDataFull.ContentReq).Type))
                {
                    msg += "，数据不完整，Type（消息类型）异常";
                }

                //工位号格式处理，确保格式统一
                webApiDataFull.webParamData.positionid = webApiDataFull.webParamData.positionid.Replace("_0X", "_0x");
                rs = string.IsNullOrEmpty(msg);
                if (webApiDataFull.IsWebSocket)
                {
                    ((WebApiDataResWS)webApiDataFull.ContentRes).Code = rs ? WebApiStatusCode.code_200 : WebApiStatusCode.code_400;
                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = rs ? WebApiStatusCode.code_200 : WebApiStatusCode.code_400;
                }
            }
            catch (Exception e)
            {
                rs = false;
                //数据处理从错误
                msg += "，" + e.Message;
                if (webApiDataFull.IsWebSocket)
                {
                    ((WebApiDataResWS)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                }
            }
            finally
            {
                try
                {
                    if (webApiDataFull.IsWebSocket)
                    {
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataResWS)webApiDataFull.ContentRes).Code) + msg;
                    }
                    else
                    {
                        ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + msg;
                    }

                    if (webApiDataFull.ContentReq != null)
                    {
                        webApiDataFull.ContentRes.RequestId = webApiDataFull.ContentReq.RequestId;
                        webApiDataFull.ContentRes.Command = webApiDataFull.ContentReq.Command;
                        webApiDataFull.ContentRes.Version = webApiDataFull.ContentReq.Version;
                        if (webApiDataFull.IsWebSocket)
                        {
                            if (rs)
                            {
                                ((WebApiDataResWS)webApiDataFull.ContentRes).Type = ((WebApiDataReqWS)webApiDataFull.ContentReq).Type.ToLower();
                            }
                            else
                            {
                                ((WebApiDataResWS)webApiDataFull.ContentRes).Type = WebApiTypeCodeWS.error.ToString();
                            }
                        }
                    }
                }
                catch
                {
                    rs = false;
                    //服务异常失败
                    if (webApiDataFull.IsWebSocket)
                    {
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5503;
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataResWS)webApiDataFull.ContentRes).Code);
                    }
                    else
                    {
                        ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5503;
                        ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code);
                    }
                }
            }

            return rs;
        }
        #endregion

        /// <summary>
        /// 通用状态返回
        /// </summary>
        /// <returns></returns>
        public CommonDataRes GetStatus()
        {
            CommonDataRes commonDataRes = new CommonDataRes();

            Dictionary<string, Object> status = new();
            status.TryAdd("wsCount", WebsocketClientManager.websocketClient.GetCount());
            commonDataRes.Data = status;

            return commonDataRes;
        }

        /// <summary>
        /// 获取客户端IP
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public string GetClientIp(HttpContext httpContext)
        {
            string ip = "";
            try
            {
                int agentType = GetAgentType(httpContext);
                //web版本访问时，由于科江使用了代理，特定传输了这两个参数
                if (agentType == 2)
                {
                    ip = httpContext.Request.Headers["X-Real-IP"].ToString();
                    if (string.IsNullOrEmpty(ip))
                    {
                        httpContext.Request.Headers["X-Forwarded-For"].ToString();
                    }
                    if (string.IsNullOrEmpty(ip))
                    {
                        ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("F", "").Replace(":", "");
                    }
                }
                else
                {
                    ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("F", "").Replace(":", "");
                }
            }
            catch (Exception e)
            { }

            return ip;
        }

        /// <summary>
        /// 获取客户端类型
        /// 0-是测试的Apifox或者Postman
        /// 1-是cs客户端（即User-Agent为空串）
        /// 2-是bs客户端
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public int GetAgentType(HttpContext httpContext)
        {
            int agentType = 0;
            try
            {
                string agent = httpContext.Request.Headers["User-Agent"].ToString();

                //是否cs客户端，用以区分是否cs客户端，因为web客户端的布局和cs客户端的不一样，需要区别开来
                //cs客户端过来的agent为空串
                //POSTMAN和APIFOX是测试
                //CSCLIENT是和客户端约定的字符串
                //agentType = (agent.ToUpper().Contains("POSTMAN") || agent.ToUpper().Contains("APIFOX")) ? 0 : ("CSCLIENT".Equals(agent.ToUpper()) ? 1 : 2);
                agentType = (agent.ToUpper().Contains("POSTMAN") || agent.ToUpper().Contains("APIFOX")) ? 0 : ("".Equals(agent.ToUpper()) ? 1 : 2);
            }
            catch (Exception e)
            { }

            return agentType;
        }

        /// <summary>
        /// 心跳服务，定时往redis发送心跳数据
        /// 用于获取当前服务副本数量
        /// </summary>
        private void HeartBeatTask()
        {
            //当前节点ID 即UUID
            string uuid = Guid.NewGuid().ToString("N");
            string keySvr = "CommandIssueService";   //心跳节点名称，保证唯一
            string lockKey = "lockKeyCommandIssueService";   //心跳节点锁key，保证唯一
            int gapSec = 2; //循环判断间隔
            int gapCheck = 20;   //判断时间差间隔，适当大于gapSec，如果大于，则判断为该节点离线，移除掉

            string server = paramInfo.GetValueOrDefault("PowerResetAddr", "10.170.1.61");

            //每次启动清空
            Thread.Sleep(new Random().Next(1, (gapSec + 1)) * 1000);
            while (true)
            {
                try
                {
                    //更新自身节点最新时间
                    redis.HashSet(keySvr, uuid, DateTime.Now);

                    //判断副本数最新时间 锁过期时间为5秒
                    redis.LockAndDo(lockKey, uuid, TimeSpan.FromSeconds(5),
                        () =>
                        {
                            //获取key下所有节点及其时间
                            Dictionary<string, DateTime> heartInfo = redis.HashGetAllDict<DateTime>(keySvr);
                            if (heartInfo != null && heartInfo.Count > 0)
                            {
                                //判断节点是否超时gapCheck，超时则移除
                                //判断当前节点ID再心跳节点的位置
                                int idx = 0;
                                foreach (string node in heartInfo.Keys)
                                {
                                    if (uuid.Equals(node))
                                    {
                                        //节点ID位置
                                        nodeNo = idx;
                                    }

                                    //判断节点过期时间
                                    if ((DateTime.Now - heartInfo[node]).TotalSeconds > gapCheck)
                                    {
                                        redis.HashDelete(keySvr, node);
                                    }
                                    else
                                    {
                                        idx++;
                                    }
                                }

                                //获得节点数
                                nodeNum = (idx == 0) ? 1 : idx;
                            }
                            return true;
                        });

                    //顺带进行ping服务
                    //if (nodeNo == 0)
                    //{
                    //    string conn = redisPower.StringGet("PowerResetConnect") ?? "0";
                    //    if ("1".Equals(conn))
                    //    {
                    //        //如果ping不通，就默认置0
                    //        bool rs = PingIpOrDomainName(server);
                    //        if (!rs)
                    //        {
                    //            mLogger.LogError("电源复位器服务器无法ping通：" + server);
                    //            redisPower.StringSetAsync("PowerResetConnect", 0);
                    //        }
                    //    }
                    //}
                }
                finally
                {
                    Thread.Sleep(gapSec * 1000);
                }
            }
        }
    }
}